
/***  DOCUMENTATION LAYER

	Kwyption Core Class
	* for use with kwyption.ini.js
	
	Last Update: Apr 2007
	Author: Tom at klenwell@gmail.com
	License: GPL License (http://www.opensource.org/licenses/gpl-license.php)

FUNCTIONS	
	ini = function(key)
	__error = function(message)
	__debug_out = function(str)
	__build_key = function(salt)
	rc4_encrypt = function(key, plaintext)
	rc4_decrypt = function(key, ciphertext)
	set_key = function(salt)
	md5 = function(salt)
	enkwypt_string = function(plaintext)
	dekwypt_string = function(kwyptstring)
	enkwack_string(plaintext)
	dekwack_string(kwackstring)
	rand(min, max)
	println(str)
	
SAMPLE USAGE
	var key = 'the key';
	var plaintext = 'testing the kwyption class';
	TheKwyptor.set_key(key);
	TheKwyptor.println("plaintext: " + plaintext, '00f');
	var kwyptstring = TheKwyptor.enkwypt_string(plaintext);
	TheKwyptor.println("kwyptstring: " + kwyptstring, '00f');
	var dekwyptstring = TheKwyptor.dekwypt_string(kwyptstring);
	TheKwyptor.println("dekwyptstring: " + dekwyptstring, '00f');

NOTES
	To link: <script type="text/javascript" src="path/to/file.js"></script>

REFERENCE
	http://developer.mozilla.org/en/docs/JavaScript

______________________________________________________________________________*/


// method: ini
/*____________________________________________________________________________*/
Kwyption.prototype.ini = function(key)
{
	// set master key
	if ( key != null ) this.set_key(key);
	
	// debug
	if ( this.debug ) this.__debug_out('this.key = ' + this.key);  /* debug */

};
/*____________________________________________________________________________*/



// method: __error
/*____________________________________________________________________________*/
Kwyption.prototype.__error = function(message)
{
	if ( this.debug ) alert("error: " + message);
	this.__fatal_error();
};
/*____________________________________________________________________________*/


// method: __debug_out
/*____________________________________________________________________________*/
Kwyption.prototype.__debug_out = function(str)
{
	this.println(str, 'ff9999');
	return;
	
}; // end method
/*____________________________________________________________________________*/


// method: __build_mkey
/*____________________________________________________________________________*/
Kwyption.prototype.__build_key = function(salt)
{
	return this.md5(salt);

}; // end method
/*____________________________________________________________________________*/


// method: rc4_encrypt
/*____________________________________________________________________________*/
Kwyption.prototype.rc4_encrypt = function(key, plaintext)
{
	// return value
	var ciphertext = '';

	// check key
	if ( key == null || key.length < 1 )
	{
		this.__error('rc4_encrypt: empty key');
		return false;
	}
	
	ciphertext = rc4_encrypt(key, plaintext);
	if ( this.debug ) this.__debug_out('[fx rc4_encrypt] ciphertext = rc4_encrypt(key, plaintext) ==> rc4_encrypt(' + key + ', ' + plaintext + ') -> ' + ciphertext);
	return ciphertext;

}; // end method
/*____________________________________________________________________________*/


// method: __rc4_decrypt
/*____________________________________________________________________________*/
Kwyption.prototype.rc4_decrypt = function(key, ciphertext)
{
	// check key
	if ( key == null || key.length < 1 )
	{
		this.__error('rc4_decrypt: empty key');
		return false;
	}
	
	return rc4_decrypt(key, ciphertext);

}; // end method
/*____________________________________________________________________________*/


// method: set_key (alias of set_mkey)
/*____________________________________________________________________________*/
Kwyption.prototype.set_key = function(salt)
{
	this.key = this.__build_key(salt);
};
/*____________________________________________________________________________*/


// method: md5
/*____________________________________________________________________________*/
Kwyption.prototype.md5 = function(salt)
{
	return hexMD5(salt);
};
/*____________________________________________________________________________*/


// method: enkwypt_string
/*____________________________________________________________________________*/
Kwyption.prototype.enkwypt_string = function(plaintext)
{
	var kwypt_string = '';	/* return */

	// kwypt-string elements
	var ikey_pos, ikey_len, ikey, cipher_1a, cipher_1b, utc, nonce;
	
	// hard parameters
	var ikey_min = 8; var ikey_max = 12;
	var delim1 = ','; var delim2 = '>'; var delim3 = '@';
	
	// vars
	var pt_len = plaintext.length;
	var DateUtc = new Date();
	utc = DateUtc.getTime();
	
	// get ikey
	var isalt_min = ( pt_len < ikey_min ) ? pt_len : ikey_min;
	var isalt_max = ( pt_len < ikey_max ) ? pt_len : ikey_max;	
	var isalt_len = this.rand(isalt_min, isalt_max);
	var isalt_pos = this.rand(0, pt_len-1 - isalt_len);
	var isalt = plaintext.substr(isalt_pos, isalt_len);
	ikey = this.rc4_encrypt(this.key, plaintext);
	ikey_len = ikey.length;
	ikey_pos = this.rand(0, pt_len-1);
		if ( this.debug ) this.__debug_out('ikey : ' + ikey + ' / ikey_len : ' + ikey_len  + ' / ikey_pos : ' + ikey_pos);
		
	// get cipher1
	var cipher = this.rc4_encrypt(ikey, plaintext);
	cipher_1a = cipher.substr(0, ikey_pos-1);
	cipher_1b = cipher.substr(ikey_pos-1);
	
	// build nonce : ikey_pos,ikey_len>cipher1a ikey cipher1b@utc
	nonce = ikey_pos + delim1 + ikey_len + delim2 + cipher_1a + ikey + cipher_1b + delim3 + utc;
		if ( this.debug ) this.__debug_out('nonce : ' + nonce);
	
	// encrypt
	kwypt_string = this.rc4_encrypt(this.key, nonce);
		if ( this.debug ) this.__debug_out('kwypt_string : ' + kwypt_string);

	return kwypt_string;
};
/*____________________________________________________________________________*/


// method: dekwypt_string
/*____________________________________________________________________________*/
Kwyption.prototype.dekwypt_string = function(kwyptstring)
{
	// return value
	var plaintext = '';
	
	// kwypt-string elements
	var nonce, ikey_pos, ikey_len, ikey, cipher_1a, cipher_1b, cipher, utc;
	var ndelim1p, ndelim2p, ndelim3p;
	
	// delims
	var delim1 = ','; var delim2 = '>'; var delim3 = '@';
	
	// vars
	var sp = ' ';

	// check key
	if ( this.key == null )
	{
		this.__error('key empty -- make sure set using this.set_key()');
		return false;
	}

	// decrypt kwyptstring
	nonce = this.rc4_decrypt(this.key, kwyptstring);
		if ( this.debug ) this.__debug_out('nonce : ' + nonce);
	
	// deconstruct nonce
	ndelim1p = nonce.indexOf(delim1);
	ikey_pos = nonce.substr(0, ndelim1p);
	var nonce_rem = nonce.substr(ndelim1p + delim1.length);
	
	ndelim2p = nonce_rem.indexOf(delim2);
	ikey_len = nonce_rem.substr(0, ndelim2p);
	nonce_rem = nonce_rem.substr(ndelim2p + delim2.length);
	
	ndelim3p = nonce_rem.lastIndexOf(delim3);
	var ciphermash = nonce_rem.substr(0, ndelim3p);
	utc = nonce_rem.substr(ndelim3p + delim3.length);
		if ( this.debug ) this.__debug_out('nonce dxd : ' + ikey_pos + sp + ikey_len + sp + ciphermash + sp + utc);
	
	// deconstruct ciphermash
	ikey = ciphermash.substr(ikey_pos-1, ikey_len);	
	cipher_1a = ciphermash.substr(0, ikey_pos - 1);
	cipher_1b = ciphermash.substr(ikey_pos*1 + ikey_len*1 - 1);
	cipher = cipher_1a + cipher_1b;
	if ( this.debug ) this.__debug_out("ciphermash dxd: " + cipher_1a + "[cipher_1a] " + ikey + "[ikey] " +  cipher_1b + "[cipher_1b]" );
	
	// decrypt kwyptstring
	var plaintext = this.rc4_decrypt(ikey, cipher);
	
	return plaintext;
};
/*____________________________________________________________________________*/


// method: enkwack_string
// builds a kwackstring: ikey{str1} + ikey + ikey{str2} + '@' + key{ikey_strpos}
// NOTE : easy to crack
/*____________________________________________________________________________*/
Kwyption.prototype.enkwack_string = function(plaintext)
{
	// return value
	var kwackstring = '';
	
	// var declarations
	var strlen = plaintext.length;
	

	// check key
	if ( this.key == null )
	{
		this.__error('key empty -- make sure set using this.set_key()');
		return false;
	}
	
	// get isalt (instring salt) (6 - 12 chars)
	var isalt_min = ( strlen < 6 ) ? strlen : 6;
	var isalt_max = ( strlen < 12 ) ? strlen : 12;	
	var isalt_len = this.rand(isalt_min, isalt_max);
	var isalt_pos = this.rand(0, strlen-1 - isalt_len);
	var isalt = plaintext.substr(isalt_pos, isalt_len);
	
	// build ikey (instring key) and set ikey_pos
	var ikey = this.__build_key(isalt);
	var ikey_pos = this.rand(1, strlen-1);
	var ikey_pos_salt = "pos" + ikey_pos;
	if ( this.debug ) this.__debug_out('ikey : ' + ikey + ' / ikey_pos : ' + ikey_pos);
	
	// rc4-encrypt string
	var ciphertext = this.rc4_encrypt(ikey, plaintext);
	if ( this.debug ) this.__debug_out('ciphertext : ' + ciphertext);

	// prepare kwyptstring components
	var ikey_pos_crypt = this.rc4_encrypt(this.key, ikey_pos_salt);
	var ciphertext1 = ciphertext.substr(0, ikey_pos-1);
	var ciphertext2 = ciphertext.substr(ikey_pos-1);   
	if ( this.debug ) this.__debug_out('ikey_pos_crypt : ' + ikey_pos_crypt);
	
	// build kwyptstring
	kwyptstring = ciphertext1 + ikey + ciphertext2 + '@' + ikey_pos_crypt;
	if ( this.debug ) this.__debug_out("kwyptstring = " + kwyptstring);

	// return	
	return kwyptstring;
	
}; // end method
/*____________________________________________________________________________*/


// method: dekwack_string
/*____________________________________________________________________________*/
Kwyption.prototype.dekwack_string = function(kwackstring)
{
	// return value
	var plaintext = '';
	
	// var declarations
	var strlen = kwackstring.length;

	// check key
	if ( this.key == null )
	{
		this.__error('key empty -- make sure set using this.set_key()');
		return false;
	}
	
	// analyze kwyptstring
	var ks_head = '';
	var ks_tail = '';
	var ks_delim_pos = kwackstring.indexOf('@');
	var ks_head = kwackstring.substr(0, ks_delim_pos);
	var ks_tail = kwackstring.substr(ks_delim_pos+1);
	if ( this.debug ) this.__debug_out('ks_head + ks_tail : ' + ks_head  + ' + ' + ks_tail);
	
	// get ikey pos
	var ikey_pos_salt = this.rc4_decrypt(this.key, ks_tail);
	var ikey_pos_str = ikey_pos_salt.replace(/pos/, '');
	ikey_pos = parseInt(ikey_pos_str);
	if ( this.debug ) this.__debug_out("ikey_pos: " + ikey_pos);
	
	// get kwyptstring components
	var ikey = ks_head.substr(ikey_pos-1, 32);	
	var ciphertext1 = ks_head.substr(0, ikey_pos - 1);
	var ciphertext2 = ks_head.substr(ikey_pos + 31);
	var ciphertext = ciphertext1 + ciphertext2;
	if ( this.debug ) this.__debug_out("kwackstring analyzed: " + ciphertext1 + "[ciphertext1] " + ikey + "[ikey] " +  ciphertext2 + "[ciphertext2] " + " @ " + ks_tail + "[ikey_pos_crypt]" );
	
	// decrypt kwyptstring
	if ( this.debug ) this.__debug_out('this.rc4_decrypt(ikey, ciphertext) : this.rc4_decrypt(' + ikey + ', ' + ciphertext + ')');
	var plaintext = this.rc4_decrypt(ikey, ciphertext);

	// return	
	return plaintext;
	
}; // end method
/*____________________________________________________________________________*/


// method: rand
/*____________________________________________________________________________*/
Kwyption.prototype.rand = function(min, max)
{
	return Math.floor(Math.random() * (max - min + 1) + min);
};
/*____________________________________________________________________________*/


// method: println
/*____________________________________________________________________________*/
Kwyption.prototype.println = function(str, color)
{
	if ( color == null ) color = 'ccc';
	var inline_style = "font:11px monospace; line-height:1.5em; color:#" + color + ";";
	document.write('<div style="' + inline_style + '">' + str + '</div>');
	return;
	
}; // end method
/*____________________________________________________________________________*/

